#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

//简单缓冲区的模拟

#define NUM 1024

struct MyFILE_{
    int fd;
    char buffer[NUM];
    int end;//缓冲区结尾
};

typedef struct MyFILE_  MyFILE;

MyFILE* fopen_(const char* pathname,const char* mode)//用系统接口进行封装，打开一个文件
{
    assert(pathname);
    assert(mode);

    MyFILE* fp = NULL;

    if(strcmp(mode,"w") == 0)//如果是“"的方式打开
    {
        int fd = open(pathname,O_WRONLY|O_TRUNC|O_CREAT,0666);//系统接口封装
        
        if(fd >= 0)//读取文件成功
        {
            fp = (MyFILE*)malloc(sizeof(MyFILE));//开辟空间
            memset(fp,0,sizeof(MyFILE));//初始化
            fp->fd = fd;//获取的fd赋值给fp中
        }
    }
    else
    {
        //无此选项
    }

    return fp;
}


void fputs_(const char*message,MyFILE *fp)
{
    assert(message);
    assert(fp);

    strcpy(fp->buffer+fp->end,message);//把字符串拷贝到我们自己的缓冲区
    fp->end += strlen(message);//更新end

    printf("%s\n",fp->buffer);
     
    if(fp->fd == 1)
    {
        //stdout输出
        if(fp->buffer[fp->end-1] == '\n')
        {
            //fprintf(stderr,"fllush:%s",fp->buffer);
            write(fp->fd,fp->buffer,fp->end);
            fp->end = 0;
        }
    }
    else
    {
        //其他文件，比如stdin(0)和stderr(2),和其他文件
    }
}

void fflush_(MyFILE* fp)
{
    assert(fp);

    if(fp->end != 0)
    {
        write(fp->fd,fp->buffer,fp->end);//写入到这个文件fd(文件描述符)中
        syncfs(fp->fd);//系统调用，写到磁盘中
        fp->end = 0;//此时缓冲区已经清空
    }
}

void fclose_(MyFILE* fp)
{
    assert(fp);
    fflush_(fp);
    close(fp->fd);//关闭这个文件
    free(fp);
}



int main()
{
    //close(1);
    MyFILE* fp = fopen_("./log.txt","w");

    if(fp == NULL)
    {
        printf("open file error\n");
        exit(1);
    }
    fputs_("one  :hello world\n",fp);
    //sleep(1);
    //fputs_("two  :hello world",fp);
    //sleep(1);
    //fputs_("three:hello world\n",fp);
    //sleep(1);
    //fputs_("four :hello world\n",fp);
    //sleep(1);
    
    fork();//创建子进程，测试：缓冲区的内容也会被父子进程进行写时拷贝，会得到两份。

    fclose_(fp);
    return 0;
}
